<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<title>Basic MVVM Sample</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock pre>code{display:block}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>Basic MVVM Sample</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_before_we_start">Before we start</a>
<ul class="sectlevel2">
<li><a href="#_prepare_your_ide">Prepare your IDE</a></li>
<li><a href="#_the_mvvm_pattern_in_short">The MVVM Pattern in short</a></li>
<li><a href="#_properties_fields_classes_and_interfaces">Properties, Fields, Classes, and Interfaces</a></li>
<li><a href="#_inotifypropertychanged">INotifyPropertyChanged</a></li>
<li><a href="#_bindings">Bindings</a></li>
<li><a href="#_create_a_new_project">Create a new Project</a></li>
</ul>
</li>
<li><a href="#_approach_1_implement_inotifypropertychanged_on_our_own">Approach 1 : Implement INotifyPropertyChanged on our own</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_simpleviewmodel">Step 1: Create SimpleViewModel</a></li>
<li><a href="#_step_2_add_properties_to_our_viewmodel">Step 2: Add Properties to our ViewModel</a></li>
<li><a href="#_step_3_add_simpleviewmodel_to_mainwindowviewmodel">Step 3: Add SimpleViewModel to MainWindowViewModel</a></li>
<li><a href="#_step_4_setup_the_view">Step 4: Setup the View</a></li>
<li><a href="#_step_5_see_it_in_action">Step 5: See it in Action</a></li>
</ul>
</li>
<li><a href="#_approach_2_using_reactiveui">Approach 2 : Using ReactiveUI</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_reactiveviewmodel">Step 1: Create ReactiveViewModel</a></li>
<li><a href="#_step_2_add_properties_to_our_viewmodel_2">Step 2: Add Properties to our ViewModel</a></li>
<li><a href="#_step_3_add_reactiveviewmodel_to_mainwindowviewmodel">Step 3: Add ReactiveViewModel to MainWindowViewModel</a></li>
<li><a href="#_step_4_modify_the_view">Step 4: Modify the View</a></li>
<li><a href="#_step_5_see_it_in_action_2">Step 5: See it in action</a></li>
</ul>
</li>
<li><a href="#_approach_3_use_any_other_mvvm_framework">Approach 3 : Use any other MVVM Framework</a></li>
<li><a href="#_related">Related</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This sample will show you how you can use the <code>MVVM</code>-pattern to receive and process text input by the user. Our sample App will have one <code>TextBox</code> where the users can enter their name and another <code>TextBox</code> where we will present some personal greetings.</p>
</div>
<!-- toc disabled -->
<h3 id="_difficulty" class="discrete">Difficulty</h3>
<div class="paragraph">
<p>🐣 Beginner 🐣</p>
</div>
<h3 id="_buzz_words" class="discrete">Buzz-Words</h3>
<div class="paragraph">
<p>MVVM, Model-View-ViewModel, ReactiveUI, INotifyPropertyChanged, XAML, Binding</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_before_we_start">Before we start</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_prepare_your_ide">Prepare your IDE</h3>
<div class="paragraph">
<p>Please make sure you have setup your IDE according to the [<a href="https://docs.avaloniaui.net/docs/get-started/set-up-an-editor" target="_blank" rel="noopener">docs</a>]. Moreover you should have read the [<a href="https://docs.avaloniaui.net/docs/welcome" target="_blank" rel="noopener">Getting Started guide</a>].</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_mvvm_pattern_in_short">The MVVM Pattern in short</h3>
<div class="paragraph">
<p>We will use the <code>MVVM</code>-pattern in this sample. <code>MVVM</code> is short for <strong>M</strong>odel - <strong>V</strong>iew - <strong>V</strong>iew<strong>M</strong>odel and describes a software architectural pattern that will split the code base into three pieces:.</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="dlist">
<dl>
<dt class="hdlist1">Model</dt>
<dd>
<p>The <code>Model</code> will store and handle your data. The model doesn&#8217;t know anything about your <code>View</code> or <code>ViewModel</code>.</p>
</dd>
<dt class="hdlist1">View</dt>
<dd>
<p>The <code>View</code> can be thought as the <strong>U</strong>ser <strong>I</strong>nterface and will interact with your <code>ViewModel</code>.</p>
</dd>
<dt class="hdlist1">ViewModel</dt>
<dd>
<p>The <code>ViewModel</code> can be seen as a mediator between the <code>View</code> and the <code>Model</code>. It will know about the <code>Model</code> and is known by the <code>View</code>, but it doesn&#8217;t know anything about the <code>View</code>. It can transform and convert the data provided by the <code>Model</code>, if needed.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="paragraph">
<p>The <code>MVVM</code> pattern helps us to focus on either of the three parts. If we get new values in our <code>Model</code>, the <code>ViewModel</code> will notify the <code>View</code> about it and the <code>View</code> can update itself. We don&#8217;t have to do it manually. If you want to read more about it, here is a collection of useful links:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.avaloniaui.net/docs/concepts/the-mvvm-pattern/" target="_blank" rel="noopener">[Avalonia Docs]</a></p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel" target="_blank" rel="noopener">[Wikipedia]</a></p>
</li>
<li>
<p><a href="https://www.codeproject.com/Articles/278901/MVVM-Pattern-Made-Simple" target="_blank" rel="noopener">[CodeProject]</a></p>
</li>
<li>
<p><a href="https://www.tutorialspoint.com/mvvm/index.htm" target="_blank" rel="noopener">[tutorialspoint]</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_properties_fields_classes_and_interfaces">Properties, Fields, Classes, and Interfaces</h3>
<div class="paragraph">
<p>C# uses <code>Interfaces</code> to define contracts that <code>Classes</code> can satisfy by providing an implementation for the <code>Members</code> defined in the specific <code>Interface</code>. <code>Members</code> of a <code>Class</code> are comprised of <code>Fields</code> and <code>Methods</code>. <code>Methods</code> are code blocks that can be executed. <code>Fields</code> are simply variables that can hold data. <code>Properties</code> are used to read, write, or compute the value of private <code>Fields</code>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>More on Interfaces: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Classes: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/class" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Members: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/members" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Methods: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/methods" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Fields: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/fields" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
<li>
<p>More on Properties: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_inotifypropertychanged">INotifyPropertyChanged</h3>
<div class="paragraph">
<p>Our <code>View</code> needs a way to know when a <code>Property</code> has changed and that the <code>View</code> should update itself. To achieve this there is an <code>Interface</code> called <code>INotifyPropertyChanged</code> which implements the event <code>PropertyChanged</code>. If our <code>ViewModel</code> implements this interface, we can send update notifications to our <code>View</code> or any other class listening to that event.</p>
</div>
<div class="paragraph">
<p>More: <a href="https://docs.microsoft.com/en-US/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-6.0" target="_blank" rel="noopener">[Microsoft Docs]</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_bindings">Bindings</h3>
<div class="paragraph">
<p>Avalonia uses <code>Controls</code> to represent functional components of a GUI Layout.  Example <code>Controls</code> include <a href="https://docs.avaloniaui.net/docs/reference/controls/scrollbar">[<code>ScrollBar</code>]</a>, [<a href="https://docs.avaloniaui.net/docs/reference/controls/buttons/button" target="_blank" rel="noopener">[<code>Button</code></a>], and [<a href="https://docs.avaloniaui.net/docs/reference/controls/detailed-reference/textbox" target="_blank" rel="noopener"><code>TextBox</code></a>]. <code>Properties</code> (like the <code>Text</code>-property of a <code>TextBox</code>) describe and allow interaction with the <code>Controls</code>.</p>
</div>
<div class="paragraph">
<p>We need a way to connect the <code>Properties</code> of our controls with the <code>Properties</code> in our <code>ViewModel</code>. Luckily we have  [<a href="https://docs.avaloniaui.net/docs/basics/data/data-binding/" target="_blank" rel="noopener"><code>Bindings</code></a>] which will do this for us. The <code>Binding</code> will update the <code>View</code> whenever the <code>ViewModel</code> reports changes and will also update the <code>ViewModel</code> whenever the user interacts with the <code>View</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_create_a_new_project">Create a new Project</h3>
<div class="paragraph">
<p>Create a new Avalonia MVVM Application using one of the following procedures, depending on your preferred development environment.</p>
</div>
<div class="sect3">
<h4 id="_microsoft_visual_studio">Microsoft Visual Studio</h4>
<div class="paragraph">
<p>Create a new "Avalonia MVVM Application" Project via <code>File ► New Project ► Avalonia MVVM Project</code>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/NewProjectVS.png" alt="New Project dialog in Microsoft Visual Studio">
</div>
</div>
</div>
<div class="sect3">
<h4 id="_jetbrains_rider">JetBrains Rider</h4>
<div class="paragraph">
<p>Create a new "Avalonia .NET Core MVVM App" Project via <code>File ► New&#8230;&#8203; ► Avalonia .NET Core MVVM App</code>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/NewProjectRider.png" alt="New Project dialog in JetBrains Rider">
</div>
</div>
</div>
<div class="sect3">
<h4 id="_command_line">Command Line</h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-txt" data-lang="txt">dotnet new avalonia.mvvm -o BasicMvvmSample
cd BasicMvvmSample</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_1_implement_inotifypropertychanged_on_our_own">Approach 1 : Implement INotifyPropertyChanged on our own</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this sample we will show you how you can implement and use the interface <code>INotifyPropertyChanged</code> on your own.</p>
</div>
<div class="sect2">
<h3 id="_step_1_create_simpleviewmodel">Step 1: Create SimpleViewModel</h3>
<div class="paragraph">
<p>Add a new class called "SimpleViewModel" to the <code>ViewModels</code> project folder. This class should implement <code>INotifyPropertyChanged</code> as shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Remember to add this to your usings
using System.ComponentModel;

namespace BasicMvvmSample.ViewModels
{
    // This is our simple ViewModel. We need to implement the interface "INotifyPropertyChanged"
    // in order to notify the View if any of our properties changed.
    public class SimpleViewModel : INotifyPropertyChanged
    {
        // This event is implemented by "INotifyPropertyChanged" and is all we need to inform
        // our view about changes.
        public event PropertyChangedEventHandler? PropertyChanged;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>For convenience we will now add a method to our class, which will raise the event for us. We need to provide the name of the property which has changed. If we add <code>[CallerMemberName]</code> to the method argument, the compiler will add the property name for us. Add the <code>using</code> statement to the top of <code>SimpleViewModel.cs</code> and the method inside the <code>SimpleViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Remember to add this to your usings
using System.Runtime.CompilerServices;

[...]

private void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_add_properties_to_our_viewmodel">Step 2: Add Properties to our ViewModel</h3>
<div class="paragraph">
<p>We want the user to be able to enter their name. This text will later be used to greet the user. So let&#8217;s add the properties <code>Name</code> and <code>Greeting</code> to our <code>ViewModel</code>.  Add the following code inside the <code>SimpleViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">private string? _Name; // This is our backing field for Name

public string? Name
{
    get
    {
        return _Name;
    }
    set
    {
        // We only want to update the UI if the Name actually changed, so we check if the value is actually new
        if (_Name != value)
        {
            // 1. update our backing field
            _Name = value;

            // 2. We call RaisePropertyChanged() to notify the UI about changes.
            // We can omit the property name here because [CallerMemberName] will provide it for us.
            RaisePropertyChanged();

            // 3. Greeting also changed. So let's notify the UI about it.
            RaisePropertyChanged(nameof(Greeting));
        }
    }
}

// Greeting will change based on a Name.
public string Greeting
{
    get
    {
        if (string.IsNullOrEmpty(Name))
        {
            // If no Name is provided, use a default Greeting
            return "Hello World from Avalonia.Samples";
        }
        else
        {
            // else greet the User.
            return $"Hello {Name}";
        }
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_add_simpleviewmodel_to_mainwindowviewmodel">Step 3: Add SimpleViewModel to MainWindowViewModel</h3>
<div class="paragraph">
<p>Remember that the <code>View</code> implements the User Interface. Our view will only consist of one [<a href="https://docs.avaloniaui.net/docs/concepts/toplevel" target="_blank" rel="noopener"><code>Window</code></a>] called <code>MainWindow</code>. Its [<a href="https://docs.avaloniaui.net/docs/basics/data/data-binding/data-context" target="_blank" rel="noopener"><code>DataContext</code></a>], which describes the default location where controls should look values when binding, is the class <code>MainWindowViewModel</code> which was already added by the template when we first created our project. We will just add an instance of our <code>SimpleViewModel</code> to it. Add the following code to the <code>MainWindowViewModel</code> class in <code>MainWindowViewModel.cs</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Add our SimpleViewModel.
// Note: We need at least a get-accessor for our Properties.
public SimpleViewModel SimpleViewModel { get; } = new SimpleViewModel();</code></pre>
</div>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">NOTE</dt>
<dd>
<p>Depending on the template and setting used to create your project you may find a class called <code>MainViewModel</code> instead of <code>MainWindowViewModel</code>. In this case please make sure the correct name.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_setup_the_view">Step 4: Setup the View</h3>
<div class="paragraph">
<p>Now we can start with the UI layout. Our View will be written in [<a href="https://docs.avaloniaui.net/docs/basics/user-interface/introduction-to-xaml" target="_blank" rel="noopener"><code>XAML</code></a>] which is an XML-based markup language that is used by many UI frameworks. The code modifications shown below will be applied to <code>MainWindow.axaml</code>.</p>
</div>
<div class="sect3">
<h4 id="_setup_the_window">Setup the Window</h4>
<div class="paragraph">
<p>Replace the content of <code>MainWindow.axaml</code> with the following code.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:BasicMvvmSample.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:CompileBindings="True"
        x:DataType="vm:MainWindowViewModel"
        x:Class="BasicMvvmSample.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="BasicMvvmSample"&gt;
&lt;/Window&gt;</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Note that the lines <code>x:CompileBindings="True"</code> and <code>x:DataType="vm:MainWindowViewModel"</code> enables [<a href="https://docs.avaloniaui.net/docs/data-binding/compiledbindings" target="_blank" rel="noopener"><code>CompiledBindings</code></a>] in our <code>Window</code>. Enabling <code>CompiledBindings</code> is completely optional but has many benefits such as better performance and a better debugging experience.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_add_the_content_to_mainwindow">Add the Content to MainWindow</h4>
<div class="paragraph">
<p>Our <code>View</code> will be built using two [<a href="https://docs.avaloniaui.net/docs/controls/textbox" target="_blank" rel="noopener"><code>TextBoxes</code></a>]: one for the user to enter their name and another one to present the greeting. As a <code>Window</code> can only have one child, we need to wrap our controls in a [<a href="https://docs.avaloniaui.net/docs/layout" target="_blank" rel="noopener"><code>Panel</code></a>]. We will choose a [<a href="https://docs.avaloniaui.net/docs/controls/stackpanel" target="_blank" rel="noopener"><code>StackPanel</code></a>], but you can use any other <code>Panel</code> variant to give you greater control over the layout.</p>
</div>
<div class="paragraph">
<p>The <code>StackPanel&#8217;s `DataContext</code> will [<a href="https://docs.avaloniaui.net/docs/data-binding/bindings" target="_blank" rel="noopener">bind</a>] to our <code>SimpleViewModel</code>. As the <code>DataContext</code> is inherited from the parent control, our <code>TextBoxes</code> will have the same <code>DataContext</code>. Therefore we can just bind <code>TextBox.Text</code> to <code>Name</code> and <code>Greeting</code> respectively.  The complete <code>MainWindow.axaml</code> code is shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:BasicMvvmSample.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:CompileBindings="True"
        x:DataType="vm:MainWindowViewModel"
        x:Class="BasicMvvmSample.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="BasicMvvmSample"&gt;

	&lt;!-- This is just used by the designer / previewer --&gt;
	&lt;Design.DataContext&gt;
        	&lt;vm:MainWindowViewModel/&gt;
    	&lt;/Design.DataContext&gt;

	&lt;!-- Our Simple ViewModel--&gt;
	&lt;StackPanel DataContext="{Binding SimpleViewModel}" Spacing="10"&gt;
		&lt;TextBlock&gt;Enter your Name:&lt;/TextBlock&gt;
		&lt;TextBox Text="{Binding Name}" /&gt;
		&lt;TextBox Text="{Binding Greeting, Mode=OneWay}"
				 IsReadOnly="True"
				 FontWeight="Bold" /&gt;
	&lt;/StackPanel&gt;
&lt;/Window&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Please note that we use <code>Mode=OneWay</code> in the <code>Binding</code> for <code>Greeting</code>, because <code>Greeting</code> is read-only and the default <code>Mode</code> of <code>TextBox.Text</code> is <code>TwoWay</code>. Moreover we made this <code>TextBox</code> read-only, so the user will not be able to change the <code>Text</code> by accident.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_see_it_in_action">Step 5: See it in Action</h3>
<div class="paragraph">
<p>In your IDE hit <code>Run</code> or <code>Debug</code>. You should see the following result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/result.png" alt="Result">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_2_using_reactiveui">Approach 2 : Using ReactiveUI</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We don&#8217;t need to implement all the boilerplate code on our own over and over again. Instead, we can use any existing <code>MVVM</code> framework out there. [<a href="https://www.reactiveui.net" target="_blank" rel="noopener">ReactiveUI</a>] is a popular MVVM framework designed for <code>.NET</code>.  If you create a new Avalonia MVVM Project, you will have <code>ReactiveUI</code> (or <code>CommunityToolkit.Mvvm</code> based on your choice) installed by default. So let&#8217;s see how we can use <code>ReactiveUI</code> to achieve the same results as <strong>Approach 1</strong> above.</p>
</div>
<div class="sect2">
<h3 id="_step_1_create_reactiveviewmodel">Step 1: Create ReactiveViewModel</h3>
<div class="paragraph">
<p>As before, we start by adding a new class called "ReactiveViewModel" into the <code>ViewModels</code> folder. This time we will use <code>ReactiveObject</code> as our base class. This base class already implements <code>INotifyPropertyChanged</code>, so we don&#8217;t need to implement it again.  Make sure your <code>ReactiveViewModel.cs</code> file looks like the code below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">using ReactiveUI;
using System;

namespace BasicMvvmSample.ViewModels
{
    // Instead of implementing "INotifyPropertyChanged" on our own we use "ReactiveObject" as
    // our base class. Read more about it here: https://www.reactiveui.net
    public class ReactiveViewModel : ReactiveObject
    {
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_add_properties_to_our_viewmodel_2">Step 2: Add Properties to our ViewModel</h3>
<div class="paragraph">
<p>Our <code>Name</code>-property now has less boilerplate code in the setter, as we can use ReactiveUI&#8217;s <code>RaiseAndSetIfChanged</code> method. Add the code below to the <code>ReactiveViewModel</code> class in <code>ReactiveViewModel.cs</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">private string? _Name; // This is our backing field for Name

public string? Name
{
    get
    {
        return _Name;
    }
    set
    {
        // We can use "RaiseAndSetIfChanged" to check if the value changed and automatically notify the UI
        this.RaiseAndSetIfChanged(ref _Name, value);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>Greeting</code> does not need to be modified and can be copied from <code>SimpleViewModel</code>.  So either copy the code below or copy the same code from <code>SimpleViewModel</code> and place it in the <code>ReactiveViewModel</code> class in <code>ReactiveViewModel.cs</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Greeting will change based on a Name.
public string Greeting
{
    get
    {
        if (string.IsNullOrEmpty(Name))
        {
            // If no Name is provided, use a default Greeting
            return "Hello World from Avalonia.Samples";
        }
        else
        {
            // else greet the User.
            return $"Hello {Name}";
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>But wait, how do we notify the <code>View</code> that <code>Greeting</code> should update? In ReactiveUI we can use [<a href="https://www.reactiveui.net/docs/handbook/when-any/#whenanyvalue" target="_blank" rel="noopener"><code>WhenAnyValue</code></a>] to listen and react to property changes. We will setup that listener in the constructor of our <code>ReactiveViewModel</code>.  Add the constructor code shown below to the <code>ReactiveViewModel</code> class.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">public ReactiveViewModel()
{
    // We can listen to any property changes with "WhenAnyValue" and do whatever we want in "Subscribe".
    this.WhenAnyValue(o =&gt; o.Name)
        .Subscribe(o =&gt; this.RaisePropertyChanged(nameof(Greeting)));
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To explain the above code in short: <code>WhenAnyValue</code> will listen to changes of the property specified in the [<a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions" target="_blank" rel="noopener">lambda</a>]. In <code>Subscribe</code> we define what should happen if the value has changed. In our case, we want to <code>RaisePropertyChanged</code> for <code>Greeting</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_add_reactiveviewmodel_to_mainwindowviewmodel">Step 3: Add ReactiveViewModel to MainWindowViewModel</h3>
<div class="paragraph">
<p>Similarly to how we added <code>SimpleViewModel</code> we can add <code>ReactiveViewModel</code> to <code>MainWindowViewModel</code>. Add the code below to the <code>MainWindowViewModel</code> class in <code>MainWindowViewModel.cs</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Add our ReactiveViewModel
public ReactiveViewModel ReactiveViewModel { get; } = new ReactiveViewModel();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_modify_the_view">Step 4: Modify the View</h3>
<div class="paragraph">
<p>We can use both <code>ViewModels</code> side by side. To demonstrate this, we will wrap both <code>Views</code> in a <code>TabControl</code>. As our properties have the same names, we can just copy &amp; paste the view we have and modify the <code>DataContext</code>.  Modify <code>MainWindow.axaml</code> to add the <code>ReactiveUI</code> view and to wrap both the <code>ReactiveUI</code> and <code>SimpleViewModel</code> controls in a <code>TabControl</code> as shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:BasicMvvmSample.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:CompileBindings="True"
        x:DataType="vm:MainWindowViewModel"
        x:Class="BasicMvvmSample.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="BasicMvvmSample"&gt;

	&lt;!-- This is just used by the designer / previewer --&gt;
    	&lt;Design.DataContext&gt;
        	&lt;vm:MainWindowViewModel/&gt;
    	&lt;/Design.DataContext&gt;

	&lt;TabControl&gt;

		&lt;!-- Our Simple ViewModel --&gt;
		&lt;TabItem Header="Simple ViewModel" &gt;
			&lt;StackPanel DataContext="{Binding SimpleViewModel}" Spacing="10"&gt;
				&lt;TextBlock&gt;Enter your Name:&lt;/TextBlock&gt;
				&lt;TextBox Text="{Binding Name}" /&gt;
				&lt;TextBox Text="{Binding Greeting, Mode=OneWay}"
						 IsReadOnly="True"
						 FontWeight="Bold" /&gt;
			&lt;/StackPanel&gt;
		&lt;/TabItem&gt;

		&lt;!-- Our ReactiveViewModel --&gt;
		&lt;TabItem Header="Reactive UI" &gt;
			&lt;StackPanel DataContext="{Binding ReactiveViewModel}" Spacing="10"&gt;
				&lt;TextBlock&gt;Enter your Name:&lt;/TextBlock&gt;
				&lt;TextBox Text="{Binding Name}" /&gt;
				&lt;TextBox Text="{Binding Greeting, Mode=OneWay}"
						 IsReadOnly="True"
						 FontWeight="Bold" /&gt;
			&lt;/StackPanel&gt;
		&lt;/TabItem&gt;

	&lt;/TabControl&gt;
&lt;/Window&gt;</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_see_it_in_action_2">Step 5: See it in action</h3>
<div class="paragraph">
<p>In your IDE hit <code>Run</code> or <code>Debug</code>. You should see the following result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/result_2.png" alt="Result">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_approach_3_use_any_other_mvvm_framework">Approach 3 : Use any other MVVM Framework</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Even though Avalonia ships ReactiveUI by default, you are not tied to it. You can use it side by side with other MVVM Frameworks or can completely replace it. Among others here is a short list of popular MVVM Frameworks for your consideration:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.reactiveui.net" target="_blank" rel="noopener">[ReactiveUI]</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction" target="_blank" rel="noopener">[CommunityToolkit.MVVM]</a></p>
</li>
<li>
<p><a href="https://docs.prismlibrary.com/docs/" target="_blank" rel="noopener">[Prism]</a></p>
</li>
<li>
<p><a href="https://caliburnmicro.com"" target="_blank" rel="noopener">[Caliburn Micro]</a></p>
</li>
<li>
<p>&#8230;&#8203; And many more.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_related">Related</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Are you looking for more advanced tutorials? Find them here:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/AvaloniaUI/Avalonia.Samples/tree/main/src/Avalonia.Samples/CompleteApps/SimpleToDoList/" target="_blank" rel="noopener">[ToDo List App]</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/docs/tutorials/music-store-app/" target="_blank" rel="noopener">[Music Store App]</a></p>
</li>
<li>
<p><a href="https://github.com/AvaloniaCommunity/awesome-avalonia#tutorials" target="_blank" rel="noopener">[Awesome-Avalonia]</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-11-07 18:43:23 UTC
</div>
</div>
</body>
</html>